/**
 *  mysql, 该包主要要用于将已存在的数据库生成对应的 proto 文件
 * 创建者： Bryce
 * 创建时间：2021/7/24
 */
package mysql

import (
	"fmt"
	"github.com/go-xorm/xorm"
	"os"
	"strings"
)

// 根据数据库表生成 proto 文件方法
func SqlToProto(engine *xorm.Engine, table, dir, pack string) error {
	// 判断是否操作单表
	if table != "" {
		return actionTable(dir, pack, table, engine)
	}
	// 获取所有表
	list, err := engine.SQL("SHOW tables").QuerySliceString()
	if err != nil {
		return err
	}
	// 循环创建表对应的 proto 文件
	for _, v := range list {
		if err := actionTable(dir, pack, v[0], engine); err != nil {
			return err
		}
	}
	return nil
}

func actionTable(dir, pack, table string, engine *xorm.Engine) error {
	// 1. 获取表字段信息
	sql := fmt.Sprintf("SHOW FULL COLUMNS FROM `%s`", table)
	list, err := engine.SQL(sql).QuerySliceString()
	if err != nil {
		return err
	}
	// 2.初始化字符串切片
	info := make([]string, 0)
	info = append(info, `syntax = "proto3";`)
	info = append(info, "")
	info = append(info, fmt.Sprintf(`package %s;`, pack))
	info = append(info, fmt.Sprintf(`option go_package="%s";`, pack))
	info = append(info, "")
	titleTable := Case2Camel(table)
	info = append(info, fmt.Sprintf("message %s {", titleTable))
	// 3. 遍历 list，组装字段结构
	for k, v := range list {
		key := k + 1
		variableType := ""
		if strings.Index(v[1], "int") != -1 {
			variableType = "int64"
		} else if strings.Index(v[1], "decimal") != -1 {
			variableType = "float64"
		} else if strings.Index(v[1], "double") != -1 {
			variableType = "float64"
		} else if strings.Index(v[1], "char") != -1 {
			variableType = "string"
		} else if strings.Index(v[1], "text") != -1 {
			variableType = "string"
		} else if strings.Index(v[1], "time") != -1 {
			variableType = "google.protobuf.Timestamp"
		} else if strings.Index(v[1], "date") != -1 {
			variableType = "google.protobuf.Timestamp"
		} else {
			variableType = "undefined"
		}
		info = append(info, fmt.Sprintf("	%s %s = %d;", variableType, v[0], key))
	}
	// 4. 组装后续字符串
	info = append(info, "}")
	info = append(info, "")
	info = append(info, fmt.Sprintf("message %sList {", titleTable))
	info = append(info, fmt.Sprintf("	repeated %s list = 1;", titleTable))
	info = append(info, "}")
	// 5. 创建文件
	return createFile(info, dir, pack+"_"+table)
}

func createFile(list []string, dir, fileName string) error {
	// 1. 创建上传文件夹
	if dir != "" {
		if err := Mkdir(dir, 0777); err != nil {
			return err
		}
	}
	// 2. 创建文件
	filePath := dir + "/" + fileName + ".proto"
	file, err := os.Create(filePath)
	if err != nil {
		return err
	}
	defer file.Close()
	// 3. 遍历字符串切片，写入文件
	for _, str := range list {
		if _, err := file.WriteString(str + "\n"); err != nil {
			return err
		}
	}
	return nil
}

// 下划线连接小写字母转成驼峰法大写字母
func Case2Camel(name string) string {
	name = strings.Replace(name, "_", " ", -1)
	name = strings.Title(name)
	return strings.Replace(name, " ", "", -1)
}

// 判断文件夹是否存在，不存在则创建
func Mkdir(path string, role os.FileMode) error {
	_, err := os.Stat(path)
	if err == nil {
		return nil
	}

	err = os.MkdirAll(path, role)
	if err != nil {
		return err
	}

	// check again
	if _, err = os.Stat(path); err == nil {
		return nil
	} else {
		return err
	}
}
